#include "zemb/RWLock.h"

void RWLock::lockR()
{
    int x;
    int success = 0;
    while (success == 0) 
    {
        x = __atomic_load_n(&m_cnt, __ATOMIC_RELAXED);
        /* write lock is held */
        if (x < 0) 
            continue;
        success = __atomic_compare_exchange_n(&m_cnt, &x, x + 1, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
    }
}

int RWLock::tryLockR()
{
    int x;
    int success = 0;
    while (success == 0)
    {
        x = __atomic_load_n(&m_cnt, __ATOMIC_RELAXED);
        /* write lock is held */
        if (x < 0)
            return -EBUSY;
        success = __atomic_compare_exchange_n(&m_cnt, &x, x + 1, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
    }

    return 0;
}

void RWLock::unLockR()
{
    __atomic_fetch_sub(&m_cnt, 1, __ATOMIC_RELEASE);
}

int RWLock::tryLockW()
{
    int x = __atomic_load_n(&m_cnt, __ATOMIC_RELAXED);
    if (x != 0 || __atomic_compare_exchange_n(&m_cnt, &x, -1, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) == 0)
        return -EBUSY;
    return 0;
}

void RWLock::lockW()
{
    int x;
    int success = 0;
    while (success == 0)
    {
        x = __atomic_load_n(&m_cnt, __ATOMIC_RELAXED);
        /* a lock is held */
        if (x != 0)
        {
            continue;
        }
        success = __atomic_compare_exchange_n(&m_cnt, &x, -1, 1,
                                              __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
    }
}

void RWLock::unLockW()
{
    __atomic_store_n(&m_cnt, 0, __ATOMIC_RELEASE);
}
